#!/usr/bin/python3
#
# Copyright 2018-2019 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
#
# Refer to the README and COPYING files for full details of the license
#
"""
This helper provides a wrapper to os_brick module, exposing
the commands needed for getting the Host connector information, attach
and detach a volume.

The available commands are:
 - "connector_info" - returns the connector information of the host
 - "attach" - attach a volume according to the connection info and return
              the device attached.
 - "detach" - detach a volume according to the device info provided.
"""

from __future__ import absolute_import
from __future__ import division

import argparse
import logging
import json
import sys

from os_brick.initiator import connector

log = logging.getLogger("managedvolume-helper")


class UsageError(Exception):
    """ Raised when usage is wrong """


def main(args):

    parser = argparse.ArgumentParser(description="Vdsm connector helper")
    parser.add_argument("--verbose", help="increase output verbosity",
                        action="store_true")
    subparsers = parser.add_subparsers(title="commands")
    info = subparsers.add_parser(
        "connector_info",
        help="Get Connector info. Returns connector info")
    info.set_defaults(command=connector_info)

    attach_parser = subparsers.add_parser(
        "attach",
        help="Attach volume. Returns attachment information")
    attach_parser.set_defaults(command=attach)

    detach_parser = subparsers.add_parser(
        "detach",
        help="Detach volume.")
    detach_parser.set_defaults(command=detach)

    args = parser.parse_args()

    logging.basicConfig(
        level=logging.DEBUG if args.verbose else logging.INFO,
        format="%(name)s: %(message)s")

    args.command(args)


def connector_info(args):
    con = connector.get_connector_properties(
                        root_helper=None,
                        my_ip=None,
                        multipath=True,
                        enforce_multipath=True)
    write_output(con)


def write_output(obj):
    sys.stdout.write(json.dumps(obj))
    sys.stdout.write('\n')
    sys.stdout.flush()


def read_input():
    data = sys.stdin.read()
    if not data:
        raise UsageError("Missing input")

    return json.loads(data)


def get_connector(conn_info):
    connector_properties = connector.get_connector_properties(
        root_helper=None,
        my_ip=None,
        multipath=True,
        enforce_multipath=True)

    protocol = conn_info['driver_volume_type']

    if protocol == "rbd":
        # Imported locally to keep non RBD tests working
        # nos_brick is importing a lot of other packages
        # that make the tests difficult
        from vdsm.storage import nos_brick

        def factory(protocol, *args, **kwargs):
            return nos_brick.RBDConnector(*args, **kwargs)
    else:
        factory = connector.InitiatorConnector.factory

    conn = factory(
        protocol,
        None,
        use_multipath=connector_properties['multipath'],
        device_scan_attempts=conn_info.get('scan_attempts', 3),
        conn=connector_properties)

    return conn


def attach(args):
    conn_info = read_input()
    log.debug("Connection info: %s", conn_info)
    conn = get_connector(conn_info)
    attachment = conn.connect_volume(conn_info['data'])
    log.debug("Attachment %s", attachment)
    write_output(attachment)


def detach(args):
    volume_info = read_input()
    log.debug("Volume info: %s", volume_info)
    conn = get_connector(volume_info["connection_info"])
    conn.disconnect_volume(volume_info['connection_info']['data'],
                           volume_info['attachment'],
                           force=False,
                           ignore_errors=False)
    write_output({})


if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))
